package com.item.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.item.annocation.SysLog;
import com.item.entity.WxSysLog;
import com.item.service.WxSysLogService;
import com.item.util.DateUtil;
import com.item.util.IPUtil;
import com.item.util.JSONUtil;
import com.item.util.RequestsUtil;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.Date;
import java.util.Objects;

/**
 * @author zhang-rongyao
 * @version V1.0
 * @Package com.item.model.interceptor
 * @date 2021/7/22 10:53
 */
@Component
public class SysLogInterceptor implements HandlerInterceptor {

    private Logger log = LoggerFactory.getLogger(SysLogInterceptor.class);

    @Autowired
    private WxSysLogService wxSysLogService;


    /**
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后，
     * 也就是DispatcherServlet渲染了视图执行，
     * 这个方法的主要作用是用于清理资源的，当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse arg1, Object arg2, Exception arg3) {
        try {
            //
            String method = request.getMethod();
            String contentType = request.getContentType();
            JSONObject jsonObject;
            if ("GET".equals(method)) {
                jsonObject = RequestsUtil.getParameterJsonInfo(request);
            } else {
                if (contentType.contains("x-www-form-urlencoded")) {
                    jsonObject = JSONObject.parseObject((InputStream) RequestsUtil.getParameterMapInfo(request), JSONObject.class);


                } else {
                    jsonObject = RequestsUtil.getRequestJsonObject(request);
                }
            }
            String clientIp = IPUtil.getClientIp(request);
            String localIP = IPUtil.getLocalIP();
            Date startTime = DateUtil.dateTimeStrToDate(MDC.get("start_time"));
            Date endTime = new Date(System.currentTimeMillis());
            WxSysLog sysLog = WxSysLog.builder()
                    .orderNo(RandomStringUtils.randomAlphabetic(15))
                    .sendTime(startTime)
                    .sendInfo(JSONUtil.toJson(jsonObject))
                    .getInfo(MDC.get("get_info"))
                    .gateIp(clientIp)
                    .getTime(endTime)
                    .build();
            wxSysLogService.saveLogs(sysLog);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MDC.clear();
        }
    }

    /**
     * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的，
     * 它的执行时间是在处理器进行处理之
     * 后，也就是在Controller的方法调用之后执行，但是它会在DispatcherServlet进行视图的渲染之前执行，
     * 也就是说在这个方法中你可以对ModelAndView进行操作。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView mv) {
    }

    /**
     * preHandle方法是进行处理器拦截用的，顾名思义，该方法将在Controller处理之前进行调用，
     * SpringMVC中的Interceptor拦截器是链式的，可以同时存在
     * 多个Interceptor，然后SpringMVC会根据声明的前后顺序一个接一个的执行，
     * 而且所有的Interceptor中的preHandle方法都会在
     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的，
     * 这种中断方式是令preHandle的返 回值为false，当preHandle的返回值为false的时候整个请求就结束了。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) {
        //获取到目标方法对象
        HandlerMethod method = (HandlerMethod) obj;
        //取到方法上的注解
        SysLog methodAnnotation = method.getMethodAnnotation(SysLog.class);

        //无此注解的一律拦截
        if (Objects.isNull(methodAnnotation)) {
            return false;
        }

        MDC.putCloseable("start_time", DateUtil.getFormatDateTime(new Date()));
        log.info("-aop 日志开始时间[{}]", MDC.get("start_time"));
        return true;
    }


}